"""
存储基类模块
定义存储接口的抽象基类
"""
import os
import uuid
from abc import ABC, abstractmethod
from typing import Optional, Dict, Any, BinaryIO, List
from pathlib import Path

from app.utils.exceptions import StorageError


class StorageBase(ABC):
    """
    存储抽象基类
    定义所有存储实现必须遵循的接口
    """
    
    def __init__(self, config: Dict[str, Any]):
        """
        初始化存储
        
        Args:
            config: 存储配置字典
        """
        self.config = config
        self.setup()
    
    @abstractmethod
    def setup(self):
        """
        设置存储环境
        在子类中实现具体的初始化逻辑
        """
        pass
    
    @abstractmethod
    def save(self, file_data: BinaryIO, filename: str, 
             folder: str = '', metadata: Dict[str, Any] = None) -> Dict[str, Any]:
        """
        保存文件
        
        Args:
            file_data: 文件数据（二进制流）
            filename: 原始文件名
            folder: 存储文件夹
            metadata: 文件元数据
            
        Returns:
            dict: 包含文件信息的字典
            
        Raises:
            StorageError: 保存失败时抛出
        """
        pass
    
    @abstractmethod
    def get(self, file_id: str) -> BinaryIO:
        """
        获取文件
        
        Args:
            file_id: 文件ID或路径
            
        Returns:
            BinaryIO: 文件二进制流
            
        Raises:
            StorageError: 文件不存在或获取失败时抛出
        """
        pass
    
    @abstractmethod
    def delete(self, file_id: str) -> bool:
        """
        删除文件
        
        Args:
            file_id: 文件ID或路径
            
        Returns:
            bool: 是否删除成功
            
        Raises:
            StorageError: 删除失败时抛出
        """
        pass
    
    @abstractmethod
    def exists(self, file_id: str) -> bool:
        """
        检查文件是否存在
        
        Args:
            file_id: 文件ID或路径
            
        Returns:
            bool: 文件是否存在
        """
        pass
    
    @abstractmethod
    def get_url(self, file_id: str, expires_in: int = 3600) -> str:
        """
        获取文件访问URL
        
        Args:
            file_id: 文件ID或路径
            expires_in: URL过期时间（秒）
            
        Returns:
            str: 文件访问URL
        """
        pass
    
    @abstractmethod
    def get_metadata(self, file_id: str) -> Dict[str, Any]:
        """
        获取文件元数据
        
        Args:
            file_id: 文件ID或路径
            
        Returns:
            dict: 文件元数据
        """
        pass
    
    @abstractmethod
    def list_files(self, prefix: str = '', limit: int = 100) -> List[Dict[str, Any]]:
        """
        列出文件
        
        Args:
            prefix: 文件前缀过滤
            limit: 返回数量限制
            
        Returns:
            list: 文件信息列表
        """
        pass
    
    def generate_filename(self, original_filename: str) -> str:
        """
        生成安全的文件名
        
        Args:
            original_filename: 原始文件名
            
        Returns:
            str: 生成的文件名
        """
        # 提取文件扩展名
        ext = os.path.splitext(original_filename)[1].lower()
        
        # 生成唯一文件名
        unique_id = uuid.uuid4().hex
        safe_filename = f"{unique_id}{ext}"
        
        return safe_filename
    
    def validate_file_size(self, file_data: BinaryIO, max_size: int = None) -> bool:
        """
        验证文件大小
        
        Args:
            file_data: 文件数据
            max_size: 最大文件大小（字节）
            
        Returns:
            bool: 文件大小是否有效
            
        Raises:
            StorageError: 文件大小超过限制时抛出
        """
        if max_size is None:
            max_size = self.config.get('max_file_size', 16 * 1024 * 1024)  # 默认16MB
        
        # 获取文件大小
        current_pos = file_data.tell()
        file_data.seek(0, 2)  # 移动到文件末尾
        file_size = file_data.tell()
        file_data.seek(current_pos)  # 恢复原位置
        
        if file_size > max_size:
            raise StorageError(f"文件大小超过限制: {file_size} > {max_size}")
        
        return True
    
    def validate_file_type(self, filename: str, allowed_types: List[str] = None) -> bool:
        """
        验证文件类型
        
        Args:
            filename: 文件名
            allowed_types: 允许的文件类型列表
            
        Returns:
            bool: 文件类型是否允许
            
        Raises:
            StorageError: 文件类型不被允许时抛出
        """
        if allowed_types is None:
            allowed_types = self.config.get('allowed_extensions', [])
        
        if not allowed_types:
            return True
        
        # 提取文件扩展名
        ext = os.path.splitext(filename)[1].lower().lstrip('.')
        
        if ext not in allowed_types:
            raise StorageError(f"文件类型不被允许: {ext}")
        
        return True
    
    def get_file_info(self, file_data: BinaryIO, filename: str) -> Dict[str, Any]:
        """
        获取文件信息
        
        Args:
            file_data: 文件数据
            filename: 文件名
            
        Returns:
            dict: 文件信息
        """
        # 获取文件大小
        current_pos = file_data.tell()
        file_data.seek(0, 2)
        file_size = file_data.tell()
        file_data.seek(current_pos)
        
        # 提取文件扩展名
        ext = os.path.splitext(filename)[1].lower().lstrip('.')
        
        return {
            'original_filename': filename,
            'file_extension': ext,
            'file_size': file_size,
            'mime_type': self._guess_mime_type(filename)
        }
    
    def _guess_mime_type(self, filename: str) -> str:
        """
        根据文件名猜测MIME类型
        
        Args:
            filename: 文件名
            
        Returns:
            str: MIME类型
        """
        ext = os.path.splitext(filename)[1].lower()
        
        mime_types = {
            '.jpg': 'image/jpeg',
            '.jpeg': 'image/jpeg',
            '.png': 'image/png',
            '.gif': 'image/gif',
            '.bmp': 'image/bmp',
            '.webp': 'image/webp',
            '.pdf': 'application/pdf',
            '.doc': 'application/msword',
            '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            '.txt': 'text/plain',
            '.zip': 'application/zip',
            '.rar': 'application/x-rar-compressed',
            '.7z': 'application/x-7z-compressed',
            '.mp3': 'audio/mpeg',
            '.mp4': 'video/mp4',
            '.avi': 'video/x-msvideo',
            '.mov': 'video/quicktime',
        }
        
        return mime_types.get(ext, 'application/octet-stream')
    
    def create_folder_structure(self, base_path: str, folder: str = '') -> str:
        """
        创建文件夹结构
        
        Args:
            base_path: 基础路径
            folder: 子文件夹
            
        Returns:
            str: 完整文件夹路径
        """
        if folder:
            full_path = os.path.join(base_path, folder)
        else:
            full_path = base_path
        
        # 创建文件夹（如果不存在）
        os.makedirs(full_path, exist_ok=True)
        
        return full_path
    
    def cleanup_old_files(self, older_than_days: int = 30):
        """
        清理旧文件
        
        Args:
            older_than_days: 清理多少天前的文件
        """
        # 在具体存储实现中重写此方法
        pass


class AsyncStorageBase(StorageBase):
    """
    异步存储抽象基类
    定义异步存储接口
    """
    
    @abstractmethod
    async def save_async(self, file_data: BinaryIO, filename: str, 
                        folder: str = '', metadata: Dict[str, Any] = None) -> Dict[str, Any]:
        """异步保存文件"""
        pass
    
    @abstractmethod
    async def get_async(self, file_id: str) -> BinaryIO:
        """异步获取文件"""
        pass
    
    @abstractmethod
    async def delete_async(self, file_id: str) -> bool:
        """异步删除文件"""
        pass
    
    @abstractmethod
    async def exists_async(self, file_id: str) -> bool:
        """异步检查文件是否存在"""
        pass
    
    @abstractmethod
    async def list_files_async(self, prefix: str = '', limit: int = 100) -> List[Dict[str, Any]]:
        """异步列出文件"""
        pass